﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using Modbus.Device;
using System.Timers;
using ModbusTest.Config;

namespace ModbusTest
{
    public class ModuleInfo
    {
        public static string[] testFunctionNames = { "皮带轮压轴承前检测", "皮带轮压轴承后检测", "皮带轮吸合面检测", "吸盘吸合面检测", "吸盘跳动检测" };
    }
    public class ModbusMaster_PLC
    {
        SerialPort _port;
        ModbusMaster _Master;
        Timer tmr = new Timer(500);
        public ModuleConfig _cfg;
        public ushort _SignalCheck_Addr;
        private byte PLC_Addr = 1;
        public int PLCNum;
        public event Action<int> RefreshUI;
        public CountManager _countManager = new CountManager() ;
        public List<CountManager> subCountManagers =new List<CountManager>();
        public event Action<string> ReadErrorEvent;
        public bool PlcModbusStatus { get; set; }
        public string ErrorMsg;

        public CountManager getSubCoubtManagerFromName(string Name)
        {
            var s = from a in subCountManagers where a.Name == Name select a;
            return s.ToArray()[0];
        }
        public void Start()
        {
            tmr.Start();
        }
        public void Stop()
        {
            tmr.Stop();
        }
        public bool Signal_Check
        {
            get
            {
                bool result =ReadSingleOut((ushort)(4096+_SignalCheck_Addr));
                return result;
            }
            set
            {
                bool SetValue = value;
                if (SetValue)
                {
                    WriteSingleOut((ushort)(4096 + _SignalCheck_Addr), 1);
                 //  _Master.WriteSingleRegister(PLC_Addr,(ushort)(4096+_SignalCheck_Addr),1);
                }
                else
                {
                    WriteSingleOut((ushort)(4096 + _SignalCheck_Addr), 0);
                  //  _Master.WriteSingleRegister(PLC_Addr, (ushort)(4096 + _SignalCheck_Addr), 0);
                }             
            }
        }
        public ModbusMaster_PLC(SerialPort port, ModuleConfig cfg,int PlcNum)
        {
            _cfg = cfg;
            _port = port;
            _Master = ModbusSerialMaster.CreateAscii(port);
            _Master.Transport.Retries = 2;   //don't have to do retries
            _Master.Transport.ReadTimeout = 300; //milliseconds            
         //   tmr.Start();
            PLCNum = PlcNum;
            tmr.Elapsed += tmr_Elapsed;
            _SignalCheck_Addr = _cfg.Signal_Check_Addr;
            _countManager.Name = ModuleInfo.testFunctionNames[PLCNum-1];
            string[] Names = (from a in _cfg.Specifications where a.PLCLocation == PLCNum select a.Description).ToArray();
            foreach (var name in Names)
            { 
                subCountManagers.Add(new CountManager(){Name=name});
            }
            ReadErrorEvent += ModbusMaster_PLC_ReadErrorEvent;
        }

        void ModbusMaster_PLC_ReadErrorEvent(string obj)
        {
            PlcModbusStatus=false;
            ErrorMsg = obj;
        }
        public void CountManagerOperation(string Name, bool result)
        {
            CountManager cm = (from a in subCountManagers where a.Name == Name select a).ToArray()[0];
            if (result)
            {
                cm.OKCount++;
            }
            else
            {
                cm.NGCount++;
            }
        }
        void tmr_Elapsed(object sender, ElapsedEventArgs e)
        {
           tmr.Stop();
            if (Signal_Check)
            {
                DoEvent();
            }

           tmr.Start();
        }

        public void DoEvent()
        {
            ReadTestValues();
            DealWithCurrentResult();
            var s = from a in _cfg.Specifications where a.PLCLocation == PLCNum select a;
            StringBuilder head = new StringBuilder();
            StringBuilder record = new StringBuilder();
            foreach (var b in s)
            {
                record.AppendFormat("{0},{1},", b.TestValue, b.TestResult?"OK":"NG");
                head.AppendFormat("{0},测试结果,", b.Description);
            }
            LogSaver.AddHeader(_cfg.Name,ModuleInfo.testFunctionNames[PLCNum - 1], head.ToString());
            LogSaver.AddLog(_cfg.Name, ModuleInfo.testFunctionNames[PLCNum - 1], record.ToString());
            RefreshUI(PLCNum);         
            while (Signal_Check)
            {
                Signal_Check = false;
                System.Threading.Thread.Sleep(10);
            }
        }
        /// <summary>
        /// 读取所有测试值
        /// </summary>
        private void ReadTestValues()
        {
            var s = from a in _cfg.Specifications where a.PLCLocation == PLCNum select a;
            foreach (var b in s)
            {
                Console.WriteLine(b.Description);
                b.TestResult =ReadSingleOut((ushort)(4096 + b.Addr_Result));
                if (b.Description != ("轴承孔直径"))
                {
                    b.TestValue = ReadSingleFloat((ushort)(4096 + b.Addr_Value));
                }
            }
        }
        private  void DealWithCurrentResult()
        {
            var s = from a in _cfg.Specifications where a.PLCLocation == PLCNum select new { a.Description,a.TestResult };
            foreach (var b in s)
            {
                CountManagerOperation(b.Description, b.TestResult);
            }
            foreach (var b in s)
            {               
                if (b.TestResult == false)
                {
                    _countManager.NGCount++;
                    return;
                }
            }
            _countManager.OKCount++;
        }

        /// <summary>
        /// 读取一个Float值
        /// </summary>
        /// <param name="Addr"></param>
        /// <returns></returns>
        private float ReadSingleFloat(ushort Addr)
        {
            try
            {
                ushort[] shortFloat = _Master.ReadHoldingRegisters(PLC_Addr, Addr, 2);
                Console.WriteLine("{0}:{1}", Addr, shortFloat[0]);
                Console.WriteLine("{0}:{1}", Addr + 1, shortFloat[1]);
                return (float)(Utils.BinaryUtil.ushortToInt(shortFloat) * (0.001));
            }
            catch (Exception ex)
            {
                if (ReadErrorEvent != null)
                    ReadErrorEvent(string.Format("PLC{0}:ex.Message",PLCNum));
                return -999;
            }
        }

        private bool ReadSingleOut(ushort Addr)
        {
         //   return _Master.ReadCoils(PLC_Addr, (ushort)(Addr), 1)[0];
            try
            {
                bool result = _Master.ReadHoldingRegisters(1, Addr, 1)[0] > 0 ? true : false;
                PlcModbusStatus = true;
                return result;
            }
            catch (Exception ex)
            {
                if (ReadErrorEvent != null)
                    ReadErrorEvent(string.Format("PLC{0}:ex.Message", PLCNum));
                return false;
            }
        }

        private void WriteSingleOut(ushort Addr,ushort value)
        {
            //   return _Master.ReadCoils(PLC_Addr, (ushort)(Addr), 1)[0];
            try
            {
                _Master.WriteSingleRegister(1, Addr, value);
                PlcModbusStatus = true;
            }
            catch (Exception ex)
            {
                if (ReadErrorEvent != null)
                    ReadErrorEvent(ex.Message);
            }
        }
        /// <summary>
        /// 读取设置的参数
        /// </summary>
        public void ReadSettingValue()
        {
           var s = from a in _cfg.Specifications where a.PLCLocation == PLCNum select a;
           foreach (var b in s)
           {
               Console.WriteLine(b.Description);
               if (b.Addr_Standard != 0)
               {
                   Console.WriteLine("标准值");
                   b.StandardValue = ReadSingleFloat((ushort)(4096 + b.Addr_Standard));  
               }
               if (b.Addr_Upper != 0)
               {
                   Console.WriteLine("上公差");
                   b.UpperOffset = ReadSingleFloat((ushort)(4096 + b.Addr_Upper));
               }
               if (b.Addr_Lower != 0)
               {
                   Console.WriteLine("下公差");
                   b.LowerOffset = ReadSingleFloat((ushort)(4096 + b.Addr_Lower));
               }              
           }
        }

        /// <summary>
        /// 连续读多个Float地址
        /// </summary>
        /// <param name="Addr"></param>
        /// <param name="Num"></param>
        /// <returns></returns>
        private float[] ReadMultiFloats(ushort Addr, ushort Num)
        {
            ushort[] shortFloats = _Master.ReadHoldingRegisters(PLC_Addr, Addr, (ushort)(2 * Num));
            float[] TestValues = new float[Num];
            for (int i = 0; i < Num; i++)
            {
                ushort[] shorts = new ushort[2];
                Array.Copy(shortFloats, i * 2, shorts, 0, 2);
                float value = Utils.BinaryUtil.UShortToFloat(shorts);
                TestValues[i] = value;
            }
            return TestValues;
        }
    }
}
